#include "string_util.h"

#include <algorithm>
#include <memory>
#include <shlobj.h>

std::string UnicodeToUtf8(const std::wstring &unicode)
{
    int len = WideCharToMultiByte(CP_UTF8, 0, unicode.c_str(), -1, nullptr, 0, nullptr, nullptr);
    if (len <= 0) {
        return "";
    }
    auto utf8 = std::make_unique<char[]>(len);
    if (utf8 == nullptr) {
        return "";
    }
    SecureZeroMemory(utf8.get(), len);

    if (WideCharToMultiByte(CP_UTF8, 0, unicode.c_str(), -1, utf8.get(), len, nullptr, nullptr) == 0) {
        return "";
    }
    std::string result(utf8.get());
    return result;
}

std::wstring Utf8ToUnicode(const std::string &utf8)
{
    int len = ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr, 0);
    if (len <= 0) {
        return L"";
    }

    auto unicode = std::make_unique<wchar_t[]>(len);
    if (unicode == nullptr) {
        return L"";
    }
    SecureZeroMemory(unicode.get(), sizeof(wchar_t) * len);

    if (::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), unicode.get(), len) == 0) {
        return L"";
    }
    std::wstring result(unicode.get());
    return result;
}

std::string UnicodeToAnsi(const std::wstring &unicode)
{
    int len = WideCharToMultiByte(CP_ACP, 0, unicode.c_str(), -1, nullptr, 0, nullptr, nullptr);
    if (len <= 0) {
        return "";
    }

    auto ansi = std::make_unique<char[]>(len);
    if (ansi == nullptr) {
        return "";
    }
    SecureZeroMemory(ansi.get(), len);

    if (::WideCharToMultiByte(CP_ACP, 0, unicode.c_str(), -1, ansi.get(), len, nullptr, nullptr) == 0) {
        return "";
    }
    std::string result(ansi.get());
    return result;
}

std::wstring AnsiToUnicode(const std::string &ansi)
{
    int len = MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), -1, 0, 0);
    if (len <= 0) {
        return L"";
    }
    auto unicode = std::make_unique<wchar_t[]>(len);
    if (unicode == nullptr) {
        return L"";
    }
    SecureZeroMemory(unicode.get(), sizeof(wchar_t) * len);

    if (MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), ansi.size(), unicode.get(), len) == 0) {
        return L"";
    }
    std::wstring result(unicode.get());
    return result;
}

std::string AnsiToUtf8(const std::string &ansi)
{
    return UnicodeToUtf8(AnsiToUnicode(ansi));
}

std::string Utf8ToAnsi(const std::string &utf8)
{
    return UnicodeToAnsi(Utf8ToUnicode(utf8));
}

std::wstring StringToWstring(const std::string &str)
{
    return AnsiToUnicode(str);
}

std::string WstringToString(const std::wstring &wstr)
{
    return UnicodeToAnsi(wstr);
}

bool FindIgnoreCase(std::string &source, std::string &target)
{
    std::transform(source.begin(), source.end(), source.begin(), tolower);
    std::transform(target.begin(), target.end(), target.begin(), tolower);
    return source.find(target) != std::wstring::npos;
}

bool FindIgnoreCase(std::wstring &source, std::wstring &target)
{
    std::transform(source.begin(), source.end(), source.begin(), tolower);
    std::transform(target.begin(), target.end(), target.begin(), tolower);
    return source.find(target) != std::wstring::npos;
}